Skip to content

Feat/mobile remote input#618

Merged
H-Chris233 merged 10 commits into
Open-Less:betafrom
ciddwd:feat/mobile-remote-input
Jun 11, 2026
Merged

Feat/mobile remote input#618
H-Chris233 merged 10 commits into
Open-Less:betafrom
ciddwd:feat/mobile-remote-input

Conversation

@ciddwd

@ciddwd ciddwd commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

User description

远程输入(局域网手机录音)

改动

新增 远程输入 功能:手机在同一局域网用浏览器打开 https://<PC-IP>:8443,得到一个录音页;手机录音以 16kHz/单声道/16-bit LE PCM 经 WebSocket(WSS) 实时推回 PC,由 Coordinator 当作"手机麦克风"喂进现有「录音 → ASR → 润色 → 光标落字」管线。识别完成后可把最终文字回传到手机端显示并一键复制;并提供「电脑落字」开关,关闭后只回传文字、不落到电脑光标。

核心约束:浏览器 getUserMedia 仅在安全上下文可用,故服务必须 HTTPS。证书用 rcgen 自签名(SAN 含本机各局域网 IP),跨重启持久化复用;TLS 走 ring 后端,避免 aws-lc-sys 的 C 编译依赖。

Rust 后端

  • remote_server/mod.rs(新增) — HTTPS + WebSocket 服务(axum + hyper + tokio-rustls);自签名证书生成/持久化/复用;6 位配对码(PIN)+ 失败锁定防爆破;capsule 状态实时转发给手机;/cert.cer/cert.mobileconfig 证书下载;/mic.png/done.png 图标路由;remote:result 文字回传转发;set_insert 落字开关控制消息。
  • coordinator.rs — 远程会话支持:remote_source_active / remote_audio_sink / remote_server / remote_pin / remote_locale / remote_no_insert 字段;start/stop/cancel_remote_dictationfeed_remote_pcmset_remote_no_insertremote_input_status
  • coordinator/dictation.rs — 远程音频源接入(跳过本地 cpal);done 时把最终文字 emit 到 remote:result;「仅回传」模式下禁用流式插入并跳过一次性落字。
  • commands.rs / lib.rs — 远程输入相关 Tauri 命令与注册。
  • types.rs — 远程输入相关类型。
  • Cargo.toml — 新增依赖:rustls / tokio-rustls / axum / hyper / hyper-util / tower / rcgen / local-ip-address

H5 录音页(编译期 include_str! 内嵌)

  • assets/app.js(新增) — WSS 连接与握手;AudioWorklet(兜底 ScriptProcessor)采集 + 线性插值重采样到 16kHz;点按/按住两种录音模式;麦克风准备 10s 超时兜底(避免 audioCtx.resume()/getUserMedia() 永久 pending 卡死);状态显示(识别中三点动效、完成对勾图);文字回传显示 + 一键复制(navigator.clipboard 优先 + execCommand 兜底);「电脑落字」开关。
  • assets/index.html / style.css(新增) — 录音页结构与样式(对齐桌面端 design tokens:黑/白/电光蓝、浅色 glassy)。
  • assets/icon.png / mic.png / done.png — 品牌 logo、麦克风、完成对勾图标。

前端(设置页与集成)

  • pages/settings/RemoteInputSection.tsx — 远程输入设置面板(开关、访问网址、配对码、证书引导)。
  • lib/ipc.ts / lib/types.ts / pages/settings/tabs.tsx — IPC 命令、类型、设置标签接入。
  • i18n/index.ts + en/ja/ko/zh-CN/zh-TW — 远程输入文案;H5 录音页语言跟随 PC 界面语言(经 set_remote_locale 同步)。

验证

  • cargo check 通过;前端 tsc 通过。
  • 实测 Windows 11:手机(局域网)打开页面、信任自签名证书、配对、录音 → 电脑光标落字 → 文字回传手机显示并一键复制。
  • 修复:H5 录音偶发卡在"正在准备麦克风…"(ensureAudio 加超时兜底);空结果框误显示([hidden] 被元素自带 display 覆盖,加全局规则根治)。

平台

  • Windows — 完整支持(已实测:点按 / 按住 / 文字回传 / 一键复制 / 电脑落字开关)。
  • macOS — 已验证(落字走 unicode keystroke)。
  • ⚠️ Linux — 服务与录音页跨平台;落字复用 fcitx5 inserter,未充分实测。

PR Type

Enhancement


Description

  • Add LAN remote voice input: phone records in browser over WSS, PC processes it through the existing dictation pipeline (ASR → polish → insert at cursor).

  • HTTPS + WebSocket server (axum/hyper/tokio-rustls) with persistent self-signed cert, 6-digit pairing PIN with lockout, and live capsule-state relay to the phone.

  • H5 recording page: AudioWorklet capture + resample to 16kHz, tap/hold modes, mic-prepare timeout guard, status UI.

  • Relay final text back to the phone with one-tap copy (clipboard + execCommand fallback).

  • "Type on computer" toggle: when off, only relay text to phone without inserting at the cursor (disables streaming insert + skips one-shot insert).

  • Frontend settings panel, IPC, and i18n in 5 languages.


Diagram Walkthrough

flowchart LR
    Phone[手机浏览器 录音页] -->|WSS 16k PCM| Server[HTTPS+WS 服务<br/>remote_server]
    Server -->|feed_remote_pcm| Coord[Coordinator 远程会话]
    Coord --> Pipeline[ASR → 润色 → 落字]
    Pipeline -->|capsule:state| Server
    Pipeline -->|remote:result 最终文字| Server
    Server -->|状态 / 文字回传| Phone
    Pipeline -->|电脑落字开关 开| Cursor[电脑光标插入]
Loading

File Walkthrough

核心服务(Enhancement)

文件 改动 行数
remote_server/mod.rs 新增 HTTPS+WS 服务、自签名证书、PIN 防爆破、capsule/文字回传转发、图标路由、落字开关控制 +718
coordinator.rs 远程会话字段与方法(start/stop/cancel、feed_pcm、no-insert 开关、状态查询) +210
coordinator/dictation.rs 远程音频源接入、remote:result 回传、禁流式 + 跳过插入 +85
commands.rs 远程输入 Tauri 命令 +36
types.rs 远程输入相关类型 +41
lib.rs 命令注册 +7
Cargo.toml rustls/axum/hyper/rcgen/local-ip-address 等依赖 +8

H5 录音页(Enhancement / Bug fix)

文件 改动 行数
assets/app.js 录音页全部逻辑:WSS、PCM 采集/重采样、超时兜底、状态/文字回传/复制、落字开关 +1413
assets/style.css 录音页样式、三点动效、复制按钮、开关、[hidden] 修复 +760
assets/index.html 录音页结构 +151
assets/icon.png / mic.png / done.png 品牌 / 麦克风 / 完成图标 bin

前端集成(Enhancement)

文件 改动 行数
pages/settings/RemoteInputSection.tsx 远程输入设置面板 +240
lib/ipc.ts 远程输入 IPC 命令 +34
lib/types.ts 类型 +8
pages/settings/tabs.tsx 设置标签接入 +2

国际化(Internationalization)

文件 改动 行数
i18n/index.ts locale 同步(H5 跟随 PC 语言) +10
i18n/{en,ja,ko,zh-CN,zh-TW}.ts 远程输入文案(5 语言) +15 each

PR Type

Enhancement, Bug fix


Description

  • Add remote input via LAN phone recording

  • HTTPS+WebSocket server with auto-cert generation

  • Integrate with coordinator for remote dictation

  • Frontend settings page and H5 UI with i18n


Diagram Walkthrough

flowchart LR
    Mobile["Mobile Browser"] -->|WSS| Server["Remote Server (HTTPS+WS)"]
    Server -->|PCM Audio| Coordinator["Coordinator"]
    Coordinator -->|Dictation Pipeline| ASR["ASR -> Polish -> Insert"]
    Coordinator -->|"remote:result"| Server
    Server -->|"result"| Mobile
Loading

File Walkthrough

Relevant files
Enhancement
17 files
index.html
Mobile H5 recording UI                                                                     
+107/-0 
mod.rs
HTTPS+WebSocket server implementation                                       
+748/-0 
coordinator.rs
Remote dictation coordinator support                                         
+245/-1 
dictation_session.rs
Remote session start with source                                                 
+65/-15 
dictation_end.rs
Remote end session and result emit                                             
+48/-8   
types.rs
Add remote input user preferences                                               
+41/-0   
settings.rs
Refresh remote server on settings change                                 
+8/-0     
lib.rs
Register remote server and new commands                                   
+7/-0     
remote_input.rs
New remote input commands                                                               
+34/-0   
mod.rs
Export remote input commands                                                         
+2/-0     
RemoteInputSection.tsx
Remote input settings component                                                   
+275/-0 
ipc.ts
Add remote input IPC functions                                                     
+34/-0   
index.ts
Sync remote locale on language change                                       
+10/-0   
types.ts
Add remote input types                                                                     
+8/-0     
tabs.tsx
Add remote input section to settings                                         
+2/-0     
app.js
Mobile H5 app logic                                                                           
+1361/-0
style.css
Mobile H5 stylesheet                                                                         
+586/-0 
I18n
5 files
ja.ts
Add remote input translations                                                       
+18/-0   
ko.ts
Add remote input translations                                                       
+18/-0   
zh-TW.ts
Add remote input translations                                                       
+18/-0   
zh-CN.ts
Add remote input translations                                                       
+18/-0   
en.ts
Add remote input translations                                                       
+18/-0   
Tests
1 files
stylePrefs.test.ts
Update test fixtures with remote prefs                                     
+4/-0     
Dependencies
1 files
Cargo.toml
Add remote server dependencies                                                     
+8/-0     

ciddwd and others added 5 commits June 6, 2026 09:07
H5 端 ensureAudio 加 10s 超时兜底:移动端 audioCtx.resume()/getUserMedia() 可能既不 resolve 也不 reject,导致 start 指令发不出、电脑端不弹胶囊、页面卡在准备麦克风;超时后 resetAudioContext 重建 AudioContext 并提示重试(新增 micTimeout 多语言文案)。其余为远程录入服务端/协调器/设置页及样式、图标等配套迭代。
…input

# Conflicts:
#	openless-all/app/src-tauri/Cargo.toml
#	openless-all/app/src-tauri/src/coordinator.rs
- 文字回传:电脑落字完成后把最终文字回传到手机 H5;一键复制(navigator.clipboard 优先 + execCommand 兜底,高兼容)
- 电脑落字开关:关闭则只回传文字、不落到电脑光标(远程会话禁流式 + 跳过一次性插入)
- 图标:录音按钮换 mic.png、完成换 done.png、识别中改三点加载动效
- 隐藏配对屏「下载证书」帮助块
- 修复 [hidden] 被元素自带 display 覆盖导致空结果框/三点照常显示
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

PR Reviewer Guide 🔍

(Review updated until commit 6e62009)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ No major issues detected

@H-Chris233

Copy link
Copy Markdown
Collaborator

有冲突和不少建议,请看一下

ciddwd added 2 commits June 10, 2026 11:44
解决与上游两次大重构的冲突:
- commands.rs 已拆为 commands/ 域模块:远程输入 4 个命令迁入新建
  commands/remote_input.rs(mod.rs glob 重导出,lib.rs 零改);
  set_settings 的远程服务 prefs diff 重启逻辑迁入 commands/settings.rs。
- coordinator/dictation.rs 已拆为 5 个子模块:远程音频源接入与跳过
  麦克风权限闸门迁入 dictation_session.rs;remote_no_insert 禁流式/
  跳插入与 remote:result 回传迁入 dictation_end.rs(适配
  streaming_insert_eligible 新增的 chinese_script_preference 参数)。
- Cargo.lock 重新生成。

验证:cargo check 通过;tsc 通过。
1. 远程标志清理收敛(修真 bug):Starting 阶段 stop 走 pending_stop 延迟
   end_session 后无人清 remote_source_active,残留 true 会让下一次本地听写
   误走远程分支(跳过 cpal、等不到手机 PCM)。抽 clear_remote_source_flags
   并补到 pending_stop 收尾与 cancel_session(幂等,本地会话 no-op)。
2. PIN 防爆破改按源 IP 锁定:pin_fails 改 HashMap<IpAddr,_>,accept loop 经
   axum Extension 注入 peer IP;带容量上限 + 过期清理,防伪造海量源 IP 撑爆。
3. 证书信任警告:mobileconfig 处注释说明安全边界(证书为非 CA 纯服务器证书,
   无签发能力);设置页新增 certTrustWarning 文案(5 语言)。

验证:cargo check 通过;cargo test --lib coordinator 相关全过(asr mock 5 例
失败为基线既有,与本改动无关);tsc 通过。
@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 6372ecd

@ciddwd

ciddwd commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

有冲突和不少建议,请看一下

冲突已解决、三项建议已处理

@H-Chris233

Copy link
Copy Markdown
Collaborator

还有两项建议...感谢跟进

之前分两次拿锁:同一 IP 的并发握手可以都先通过锁定检查、再各自累计失败,
让计数越过 PIN_MAX_FAILS 却不触发锁定。合并后并发请求无法在检查与累计之间
穿插。PIN 比较本身无共享状态,留在锁外。

另:cancel_remote_dictation 补注释说明 double-cancel(stop 收尾后断连)下
cancel_session 经 begin_cancel_session_state 返回 None 早退,是安全 no-op
(review 提到的 panic 不存在)。

验证:cargo check 通过。
@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit d3f2e33

1. 错误粒度:remote-input:error 携带的 reason 之前被前端忽略,任何启动失败
   (TLS/权限/证书生成)都显示「端口被占用」误导排查。现在仅 reason ==
   'port-in-use' 时显示端口文案,其余显示通用 startError + 原始 reason
   (5 语言)。
2. load_or_generate_cert 残留 v3 时代「做成自签名 CA」的过时注释,与下方
   v4「非 CA 服务器证书」的实现矛盾(review 机器人因此误判 iOS 不可用)。
   统一为:主路径页面级例外,cert.cer/mobileconfig 是系统级安装兜底。

验证:cargo check 通过;tsc 通过。
@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 23cc80e

按评审机器人的维度(安全/并发/资源/错误处理/边界)对远程输入全部改动面
做了一轮地毯式自审,修复如下:

Rust 核心(2 HIGH + 3 medium):
- [HIGH] start_remote_dictation TOCTOU:busy 预检查与 remote_source_active
  置位原在锁外,竞态输家把残留标志泄给抢先启动的本地会话(被劫持进远程
  分支:不开麦克风、听写全文经 remote:result 泄给手机)。新增
  begin_session_with_source(remote),busy 判定与置位移入 begin_session_state
  同一临界区(与本地路径同构);busy 时返回 Err(REMOTE_BUSY) 供手机回执。
- [HIGH] 服务关停/重置 PIN 不撤销存量连接:连接任务是裸 spawn,shutdown 只
  停 accept loop,已配对手机仍能录音落字。新增 watch 关停广播,shutdown 时
  断开全部存量 WS 连接(旧 handle 被覆盖时靠 sender drop 自动生效)。
- end_session 新增 RemoteFlagsJanitor(Drop 兜底):十余处终结路径统一在
  会话回 Idle 时清远程标志;同时修复 double-stop 过早清标志导致「仅回传」
  失效与 remote:result 丢失(phase 非 Idle 不清)。
- stop/cancel_remote_dictation 加守卫:手机 stop/断连不再能终止 PC 用户
  正在进行的本地听写。
- refresh_remote_server 串行化(异步锁 + 代数):连点开关/连改端口不再交错
  启动、误报 port-in-use;中间代直接让位只跑最后一轮。
- WS 主循环加 keepalive(30s Ping / 90s 无上行断开):手机息屏不发 FIN 时
  半开连接与录音会话不再悬挂;PCM 单帧加 64KB 上限防认证后 DoS。
- 私钥 remote-key-v4.der 在 Unix 下收紧为 0600。

H5 录音页(4 medium + 1 low):
- getUserMedia 超时后迟到 resolve 用代际计数作废,不再泄漏麦克风轨道;
- hold 按下立即松手不再发孤立 stop(startSent 配对),transcribing/polishing
  加 30s 客户端兜底超时,UI 不再可能永久卡「识别中…」;
- iOS 来电/Siri 后的 'interrupted' 状态改为非 running 即 resume;
- sessionStorage 被禁时写后读回校验,根除无限 reload 循环;
- readyTimer/busyTimer 统一跟踪清理,杜绝迟到定时器错盖新状态。

设置页(2 medium + 1 low):
- listen() 异步注册 vs 卸载竞态:注册完成若已卸载立即退订;
- 端口输入改草稿+失焦提交:不再逐键重启服务,clamp 对齐 [1024,65535],
  取整防 u16 反序列化失败;
- 重置 PIN 用命令返回值本地更新,不再撞异步重启窗口闪烁,并加错误处理。

验证:cargo check 0 错误;cargo test --lib 508 过(5 例失败为基线既有 asr
mock);tsc 通过;node --check app.js 通过。
@github-actions

Copy link
Copy Markdown

Persistent review updated to latest commit 6e62009

@H-Chris233 H-Chris233 merged commit 91023ed into Open-Less:beta Jun 11, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants